//
//  FFKVOProxy.m
//  FFMVVM
//
//  Created by 李魁峰 on 2016/12/6.
//  Copyright © 2016年 AKsoftware. All rights reserved.
//

#import "FFKVOProxy.h"
#import "FFBindUnit.h"

@interface FFKVOProxy ()
@property (strong, nonatomic) NSMapTable<FFKVOBindUnit*,FFKVOBindUnit*> *kvoUnits;
@end

@implementation FFKVOProxy

#pragma mark public

#pragma mark kvo


- (void)bindUnit:(FFKVOBindUnit *)unit
{
    [self destoryBindUnit:unit];
    if (unit.target) {
        @synchronized (self)
        {
            [self.kvoUnits setObject:unit forKey:unit];
        }
        
        for (NSString *key in unit.keyPathArray)
        {
            [unit.target addObserver:self
                          forKeyPath:key
                             options:unit.options
                             context:(void *)unit];
        }
    }
}

- (void)destoryBindUnit:(FFKVOBindUnit *)unit
{
//    FFKVOBindUnit *valueUnit = [self.bindTable objectForKey:unit];
//    if (valueUnit && valueUnit.target) {
//        for (NSString *key in valueUnit.keyPathArray) {
//            [valueUnit.target removeObserver:self forKeyPath:key context:(void *)unit];
//        }
//    }
//    [self removeUnit:unit];
}


- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSKeyValueChangeKey,id> *)change
                       context:(void *)context
{
    FFKVOBindUnit *unit = nil;
    @synchronized (self) {
        unit = [self.kvoUnits objectForKey:(__bridge id)(context)];
    }
    
    [unit perfomKvoBlock:keyPath object:[object valueForKeyPath:keyPath]];
    
    if (unit)
    {
        
        void (^block)(NSString *key, id value);
        NSThread *thread;
        dispatch_queue_t queue;
        @synchronized (self) {
            block = unit.performBlock;
            thread = unit.performThread;
            queue = unit.performQueue;
        }
        
        if (thread) {
            if ([NSThread currentThread] == thread) {
                if (block) {
                    block(key, value);
                }
            } else {
                NSMutableDictionary *dic = [NSMutableDictionary dictionary];
                if (key) {
                    [dic setObject:key forKey:@"key"];
                }
                if (value) {
                    [dic setObject:value forKey:@"value"];
                }
                [self performSelector:@selector(threadPerformBlock:) onThread:thread withObject:dic waitUntilDone:NO];
            }
            return;
        }
        if (queue) {
            if ([[NSThread currentThread] isMainThread] && queue == dispatch_get_main_queue()) {
                if (block) {
                    block(key, value);
                }
            } else {
                dispatch_async(queue, ^{
                    if (block) {
                        block(key, value);
                    }
                });
            }
            return;
        }
        
        if (block) {
            block(key, value);
        }
        
        
        
    }
}


- (void)perfomKvoBlock:(NSString *)key object:(id)value
{
    void (^block)(NSString *key, id value);
    NSThread *thread;
    dispatch_queue_t queue;
    @synchronized (self) {
        block = self.performBlock;
        thread = self.performThread;
        queue = self.performQueue;
    }
    
    if (thread) {
        if ([NSThread currentThread] == thread) {
            if (block) {
                block(key, value);
            }
        } else {
            NSMutableDictionary *dic = [NSMutableDictionary dictionary];
            if (key) {
                [dic setObject:key forKey:@"key"];
            }
            if (value) {
                [dic setObject:value forKey:@"value"];
            }
            [self performSelector:@selector(threadPerformBlock:) onThread:thread withObject:dic waitUntilDone:NO];
        }
        return;
    }
    if (queue) {
        if ([[NSThread currentThread] isMainThread] && queue == dispatch_get_main_queue()) {
            if (block) {
                block(key, value);
            }
        } else {
            dispatch_async(queue, ^{
                if (block) {
                    block(key, value);
                }
            });
        }
        return;
    }
    
    if (block) {
        block(key, value);
    }
}

- (void)threadPerformBlock:(NSMutableDictionary *)dic
{
    void (^block)(NSString *key, id value);
    @synchronized (self) {
        block = self.performBlock;
    }
    if (block) {
        block(dic[@"key"], dic[@"value"]);
    }
}

#pragma mark lazy load

- (NSMapTable *) kvoUnits
{
    if (!_kvoUnits)
    {
        _kvoUnits = [NSMapTable strongToWeakObjectsMapTable];
    }
    return _kvoUnits;
}
@end
